﻿namespace OpenAdmin.Application.Services;

/// <summary>
/// 权限相关功能服务
/// </summary>
public class PermissionService : ServiceBase, IPermissionService, ITransientDependency
{
    private readonly IRepository<SysUser, ApplicationDbContextLocator> _userRepository;
    private readonly IRepository<SysUserRole, ApplicationDbContextLocator> _userRoleRepository;
    private readonly IRepository<SysRole, ApplicationDbContextLocator> _roleRepository;
    private readonly IRepository<SysRolePermission, ApplicationDbContextLocator> _rolePermissionRepository;
    private readonly IRepository<SysPermission, ApplicationDbContextLocator> _permissionRepository;
    private readonly IRepository<SysPermissionOperation, ApplicationDbContextLocator> _permissionOperationRepository;
    private readonly IRepository<SysOperation, ApplicationDbContextLocator> _operationRepository;
    private readonly IRepository<SysApiResource, ApplicationDbContextLocator> _apiResourceRepository;
    private readonly IRepository<SysPermissionMenu, ApplicationDbContextLocator> _permissionMenuRepository;
    private readonly IRepository<SysMenu, ApplicationDbContextLocator> _menuRepository;
    /// <summary>
    /// 构造函数
    /// </summary>
    /// <param name="userRepository"></param>
    /// <param name="userRoleRepository"></param>
    /// <param name="roleRepository"></param>
    /// <param name="rolePermissionRepository"></param>
    /// <param name="permissionRepository"></param>
    /// <param name="permissionOperationRepository"></param>
    /// <param name="operationRepository"></param>
    /// <param name="apiResourceRepository"></param>
    /// <param name="permissionMenuRepository"></param>
    /// <param name="menuRepository"></param>
    public PermissionService(IRepository<SysUser, ApplicationDbContextLocator> userRepository,
        IRepository<SysUserRole, ApplicationDbContextLocator> userRoleRepository,
        IRepository<SysRole, ApplicationDbContextLocator> roleRepository,
        IRepository<SysRolePermission, ApplicationDbContextLocator> rolePermissionRepository,
        IRepository<SysPermission, ApplicationDbContextLocator> permissionRepository,
        IRepository<SysPermissionOperation, ApplicationDbContextLocator> permissionOperationRepository,
        IRepository<SysOperation, ApplicationDbContextLocator> operationRepository,
        IRepository<SysApiResource, ApplicationDbContextLocator> apiResourceRepository,
        IRepository<SysPermissionMenu, ApplicationDbContextLocator> permissionMenuRepository,
        IRepository<SysMenu, ApplicationDbContextLocator> menuRepository
        )
    {
        _userRepository = userRepository;
        _userRoleRepository = userRoleRepository;
        _roleRepository = roleRepository;
        _rolePermissionRepository = rolePermissionRepository;
        _permissionRepository = permissionRepository;
        _permissionOperationRepository = permissionOperationRepository;
        _operationRepository = operationRepository;
        _apiResourceRepository = apiResourceRepository;
        _permissionMenuRepository = permissionMenuRepository;
        _menuRepository = menuRepository;
    }

    /// <summary>
    /// 获取指定用户权限
    /// </summary>
    /// <param name="userId"></param>
    /// <param name="ignoreQueryFilters"></param>
    /// <param name="hasSuperAdmin"></param>
    /// <returns></returns>
    /// <exception cref="NotImplementedException"></exception>
    public async Task<List<UserPermissionResponseDto>> GetUserPermissions(int userId,
        bool ignoreQueryFilters = false, bool hasSuperAdmin = false)
    {
        var userQueryable = _userRepository.Entities.AsQueryable();
        var roleQueryable = _roleRepository.Entities.AsQueryable();
        if (ignoreQueryFilters)
        {
            userQueryable = userQueryable.Where(p => !p.IsDeleted).IgnoreQueryFilters();
            roleQueryable = roleQueryable.Where(p => !p.IsDeleted).IgnoreQueryFilters();
        }
        var dtos = await (from user in userQueryable
                          join userRole in _userRoleRepository.Entities.AsQueryable() on user.Id equals userRole.UserId
                          join role in roleQueryable on userRole.RoleId equals role.Id
                          join rolePermission in _rolePermissionRepository.Entities.AsQueryable() on role.Id equals rolePermission.RoleId
                          join permission in _permissionRepository.Entities.AsQueryable() on rolePermission.PermissionId equals permission.Id
                          join permissionOperation in _permissionOperationRepository.Entities.AsQueryable() on permission.Id equals permissionOperation.PermissionId
                          join operation in _operationRepository.Entities.AsQueryable() on permissionOperation.OperationId equals operation.Id
                          join apiResource in _apiResourceRepository.Entities.AsQueryable() on operation.ApiResourceId equals apiResource.Id into apiResource_join
                          from apiResource in apiResource_join.DefaultIfEmpty()
                          where user.Id == userId
                          select new UserPermissionResponseDto
                          {
                              RoleId = role.Id,
                              RoleName = role.Name,
                              LinkUrl = apiResource.LinkUrl
                          }).ToListAsync();
        return dtos;
    }

    /// <summary>
    /// 获取权限列表
    /// </summary>
    /// <returns></returns>
    /// <exception cref="NotImplementedException"></exception>
    public async Task<List<PermissionResponseDto>> GetPermissions()
    {
        var userId = UserIdentity.UserId;
        var permissionMenus = await GetPermissionMenus(userId).ToListAsync();
        var permissionOperations = await GetPermissionOperations(userId).ToListAsync();
        var parentPermissions = permissionMenus.Where(p => p.ParentId == 0).ToList();
        foreach (var permissionMenu in permissionMenus)
        {
            permissionMenu.Operations = permissionOperations.Where(p => p.MenuId == permissionMenu.MenuId).ToList();
        }
        RecursionChildrenPermission(permissionMenus, parentPermissions);
        return parentPermissions;
    }

    /// <summary>
    /// 获取角色权限列表
    /// </summary>
    /// <param name="roleId"></param>
    /// <returns></returns>
    /// <exception cref="NotImplementedException"></exception>
    public async Task<RolePermissionResponseDto> GetRolePermissions(int roleId)
    {
        var permisMenus = await GetPermissionByRole(roleId, SysConst.MENU).ToListAsync();
        var permisOperations = await GetPermissionByRole(roleId, SysConst.OPERATION).ToListAsync();
        var result = new RolePermissionResponseDto
        {
            MenuPermissionIds = permisMenus.Select(p => p.Id).ToList(),
            OperationPermissionIds = permisOperations.Select(p => p.Id).ToList()
        };
        return result;
    }

    /// <summary>
    /// 获取租户权限列表
    /// </summary>
    /// <param name="tenantId"></param>
    /// <returns></returns>
    public async Task<TenantPermissionResponseDto> GetTenantPermissions(Guid tenantId)
    {
        var dto = new TenantPermissionResponseDto();
        var role = await GetSystemAdminRoleByTenant(tenantId);
        if (role == null)
        {
            return dto;
        }
        var data = await GetRolePermissions(role.Id);
        dto.MenuPermissionIds = data.MenuPermissionIds;
        dto.OperationPermissionIds = data.OperationPermissionIds;
        return dto;
    }

    /// <summary>
    /// 获取用户权限菜单
    /// </summary>
    /// <returns></returns>
    public async Task<List<PermissionMenuResponseDto>> GetUserPermissionMenus()
    {
        IQueryable<SysMenu> menus = null;
        if (UserIdentity.HasSuperAdmin)
        {
            menus = from permission in _permissionRepository.Entities.AsQueryable()
                    join permissionMenu in _permissionMenuRepository.Entities.AsQueryable() on permission.Id equals permissionMenu.PermissionId
                    join menu in _menuRepository.Entities.AsQueryable() on permissionMenu.MenuId equals menu.Id
                    where permission.Type == SysConst.MENU
                    select menu;
        }
        else
        {
            menus = from user in _userRepository.Entities.AsQueryable()
                    join userRole in _userRoleRepository.Entities.AsQueryable() on user.Id equals userRole.UserId
                    join role in _roleRepository.Entities.AsQueryable() on userRole.RoleId equals role.Id
                    join rolePermission in _rolePermissionRepository.Entities.AsQueryable() on role.Id equals rolePermission.RoleId
                    join permission in _permissionRepository.Entities.AsQueryable() on rolePermission.PermissionId equals permission.Id
                    join permissionMenu in _permissionMenuRepository.Entities.AsQueryable() on permission.Id equals permissionMenu.PermissionId
                    join menu in _menuRepository.Entities.AsQueryable() on permissionMenu.MenuId equals menu.Id
                    where user.Id == UserIdentity.UserId && permission.Type == SysConst.MENU
                    select menu;
        }
        var userPermisMenus = await menus.Select(p => new PermissionMenuResponseDto
        {
            Id = p.Id,
            Name = p.Name,
            ParentId = p.ParentId,
            Sort = p.Sort,
            Path = p.Route,
            Icon = p.Icon
        }).ToListAsync();
        userPermisMenus = userPermisMenus.DistinctBy(p => p.Id).ToList();
        var parentMenu = userPermisMenus.Where(p => p.ParentId == 0).ToList();
        RecursionChildrenMenu(userPermisMenus, parentMenu);

        return parentMenu;
    }

    /// <summary>
    /// 保存角色权限
    /// </summary>
    /// <param name="requestDto"></param>
    /// <returns></returns>
    public async Task SaveRolePermission(SaveRolePermissionRequestDto requestDto)
    {
        var roleQueryable = _roleRepository.Entities.AsQueryable();
        var rolePermissionQueryable = _rolePermissionRepository.Entities.AsQueryable();
        var permissionQueryable = _permissionRepository.Entities.AsQueryable();

        SysRole roleInfo = null;
        if (UserIdentity.HasSuperAdmin)
        {
            roleQueryable = roleQueryable.Where(p => !p.IsDeleted).IgnoreQueryFilters();
            rolePermissionQueryable = rolePermissionQueryable.IgnoreQueryFilters();
            permissionQueryable = permissionQueryable.Where(p => !p.IsDeleted).IgnoreQueryFilters();
            roleInfo = await roleQueryable.SingleOrDefaultAsync(p => p.Id == requestDto.RoleId);
        }
        else
        {
            roleInfo = await roleQueryable.SingleOrDefaultAsync(p => p.Id == requestDto.RoleId);
        }

        roleInfo.ThrowIfNull("角色不存在");

        var rolePermissions = await (from r in roleQueryable
                                     join rp in rolePermissionQueryable on r.Id equals rp.RoleId
                                     join p in permissionQueryable on rp.PermissionId equals p.Id
                                     where r.Id == roleInfo.Id
                                     select rp).ToListAsync();

        var removeRolePermissions = rolePermissions.Where(p => !requestDto.PermissionIds.Contains(p.PermissionId)).ToList();
        _rolePermissionRepository.Entities.RemoveRange(removeRolePermissions);

        var permissionIds = rolePermissions.Select(p => p.PermissionId).ToList();
        var addRolePermissions = requestDto.PermissionIds.Where(id => !permissionIds.Contains(id)).Select(p => new SysRolePermission
        {
            RoleId = roleInfo.Id,
            PermissionId = p
        }).ToList();
        await _rolePermissionRepository.Entities.AddRangeAsync(addRolePermissions);

        if (removeRolePermissions.Count > 0 || addRolePermissions.Count > 0)
        {
            var result = await _operationRepository.SaveChangesAsync();
            result.ThrowIf(p => p == 0, "保存失败");
        }
    }

    /// <summary>
    /// 保存租户系统管理员权限
    /// </summary>
    /// <param name="requestDto"></param>
    /// <returns></returns>
    public async Task SaveTenantPermission(SaveTenantPermissionRequestDto requestDto)
    {
        var role = await GetSystemAdminRoleByTenant(requestDto.TenantId);
        role.ThrowIfNull("当前租户还未创建角色，无法分配权限");

        await SaveRolePermission(new SaveRolePermissionRequestDto
        {
            RoleId = role.Id,
            PermissionIds = requestDto.PermissionIds
        });
    }
    #region Private Methods
    /// <summary>
    /// 获取权限菜单
    /// </summary>
    /// <param name="userId"></param>
    /// <returns></returns>
    private IQueryable<PermissionResponseDto> GetPermissionMenus(int userId)
    {
        if (UserIdentity.HasSuperAdmin)
        {
            return from permission in _permissionRepository.Entities.AsQueryable()
                   join permissionMenu in _permissionMenuRepository.Entities.AsQueryable() on permission.Id equals permissionMenu.PermissionId
                   join menu in _menuRepository.Entities.AsQueryable() on permissionMenu.MenuId equals menu.Id
                   where permission.Type == SysConst.MENU
                   select new PermissionResponseDto
                   {
                       Id = permission.Id,
                       MenuId = menu.Id,
                       Name = menu.Name,
                       ParentId = menu.ParentId,
                       Type = permission.Type
                   };
        }
        return from user in _userRepository.Entities
               join userRole in _userRoleRepository.Entities on user.Id equals userRole.UserId
               join role in _roleRepository.Entities on userRole.RoleId equals role.Id
               join rolePermission in _rolePermissionRepository.Entities on role.Id equals rolePermission.RoleId
               join permission in _permissionRepository.Entities on rolePermission.PermissionId equals permission.Id
               join permissionMenu in _permissionMenuRepository.Entities on permission.Id equals permissionMenu.PermissionId
               join menu in _menuRepository.Entities on permissionMenu.MenuId equals menu.Id
               where user.Id == userId && permission.Type == SysConst.MENU
               select new PermissionResponseDto
               {
                   Id = permission.Id,
                   MenuId = menu.Id,
                   Name = menu.Name,
                   ParentId = menu.ParentId,
                   Type = permission.Type
               };

    }

    /// <summary>
    /// 获取权限功能
    /// </summary>
    /// <param name="userId"></param>
    /// <returns></returns>
    private IQueryable<PermissionResponseDto> GetPermissionOperations(int userId)
    {
        if (UserIdentity.HasSuperAdmin)
        {
            return from permission in _permissionRepository.Entities.AsQueryable()
                   join permissionOperation in _permissionOperationRepository.Entities.AsQueryable() on permission.Id equals permissionOperation.PermissionId
                   join operation in _operationRepository.Entities.AsQueryable() on permissionOperation.OperationId equals operation.Id
                   where permission.Type == SysConst.OPERATION
                   select new PermissionResponseDto
                   {
                       Id = permission.Id,
                       MenuId = operation.MenuId,
                       Name = operation.Name,
                       ParentId = operation.ParentId,
                       Type = permission.Type
                   };
        }
        return from user in _userRepository.Entities
               join userRole in _userRoleRepository.Entities on user.Id equals userRole.UserId
               join role in _roleRepository.Entities on userRole.RoleId equals role.Id
               join rolePermission in _rolePermissionRepository.Entities on role.Id equals rolePermission.RoleId
               join permission in _permissionRepository.Entities on rolePermission.PermissionId equals permission.Id
               join permissionOperation in _permissionOperationRepository.Entities.AsQueryable() on permission.Id equals permissionOperation.PermissionId
               join operation in _operationRepository.Entities.AsQueryable() on permissionOperation.OperationId equals operation.Id
               where user.Id == userId && permission.Type == SysConst.OPERATION
               select new PermissionResponseDto
               {
                   Id = permission.Id,
                   MenuId = operation.MenuId,
                   Name = operation.Name,
                   ParentId = operation.ParentId,
                   Type = permission.Type
               };
    }

    /// <summary>
    /// 递归子权限项
    /// </summary>
    /// <param name="all"></param>
    /// <param name="parent"></param>
    private void RecursionChildrenPermission(List<PermissionResponseDto> all, List<PermissionResponseDto> parent)
    {
        foreach (var item in parent)
        {
            var subMenus = all.Where(p => p.ParentId == item.MenuId).ToList();
            foreach (var subMenu in subMenus)
            {
                item.Children = item.Children == null ? new List<PermissionResponseDto>() : item.Children;
                item.Children.Add(subMenu);
            }
            RecursionChildrenPermission(all, subMenus);
        }
    }

    /// <summary>
    /// 递归子菜单项
    /// </summary>
    /// <param name="all"></param>
    /// <param name="parent"></param>
    private void RecursionChildrenMenu(List<PermissionMenuResponseDto> all, List<PermissionMenuResponseDto> parent)
    {
        foreach (var item in parent)
        {
            item.Meta = new PermissionMenuMeta
            {
                Icon = item.Icon,
                Title = item.Name
            };
            var subMenus = all.Where(p => p.ParentId == item.Id).ToList();
            item.IsMenuItem = item.ParentId == 0 && !subMenus.Any();
            foreach (var subMenu in subMenus)
            {
                item.Children.Add(subMenu);
            }
            RecursionChildrenMenu(all, subMenus);
        }
    }

    /// <summary>
    /// 根据角色获取权限
    /// </summary>
    /// <param name="roleId"></param>
    /// <param name="permissionType"></param>
    /// <returns></returns>
    private IQueryable<PermissionResponseDto> GetPermissionByRole(int roleId, string permissionType)
    {
        var roleQueryable = _roleRepository.Entities.AsNoTracking();
        if (UserIdentity.HasSuperAdmin)
        {
            roleQueryable = roleQueryable.Where(p => !p.IsDeleted).IgnoreQueryFilters();
        }
        IQueryable<PermissionResponseDto> permissions = null;
        if (permissionType == SysConst.MENU)
        {
            permissions = from r in roleQueryable
                          join rp in _rolePermissionRepository.Entities on r.Id equals rp.RoleId
                          join p in _permissionRepository.Entities on rp.PermissionId equals p.Id
                          join pm in _permissionMenuRepository.Entities on p.Id equals pm.PermissionId
                          join m in _menuRepository.Entities on pm.MenuId equals m.Id
                          where r.Id == roleId && p.Type == permissionType
                          select new PermissionResponseDto
                          {
                              Id = p.Id,
                              ParentId = m.ParentId
                          };
        }
        else if (permissionType == SysConst.OPERATION)
        {
            permissions = from r in roleQueryable
                          join rp in _rolePermissionRepository.Entities on r.Id equals rp.RoleId
                          join p in _permissionRepository.Entities on rp.PermissionId equals p.Id
                          join po in _permissionOperationRepository.Entities on p.Id equals po.PermissionId
                          join o in _operationRepository.Entities on po.OperationId equals o.Id
                          where r.Id == roleId && p.Type == permissionType
                          select new PermissionResponseDto
                          {
                              Id = p.Id,
                              ParentId = o.ParentId
                          };
        }
        return permissions;
    }

    /// <summary>
    /// 获取租户的系统管理员角色
    /// </summary>
    /// <param name="tenantId"></param>
    /// <returns></returns>
    private async Task<SysRole> GetSystemAdminRoleByTenant(Guid tenantId)
    {
        if (tenantId == Guid.Empty)
        {
            return null;
        }
        var tenantAdmin = await _userRepository.Entities.AsNoTracking()
                .Where(p => !p.IsDeleted && p.TenantId == tenantId && p.UserType == UserType.SystemAdmin).IgnoreQueryFilters().FirstOrDefaultAsync();
        if (tenantAdmin == null)
        {
            return null;
        }
        var role = await (from r in _roleRepository.Entities.AsNoTracking().Where(p => !p.IsDeleted).IgnoreQueryFilters()
                          join ur in _userRoleRepository.Entities.AsNoTracking() on r.Id equals ur.RoleId
                          where ur.UserId == tenantAdmin.Id
                          select r).FirstOrDefaultAsync();
        return role;
    }
    #endregion

}
